package cn.dansj.service.Impl;

import cn.dansj.bean.ArticleTitle;
import cn.dansj.common.utils.json.DictMap;
import cn.dansj.entity.Article;
import cn.dansj.entity.ArticleTag;
import cn.dansj.repository.ArticleRepository;
import cn.dansj.service.ArticleService;
import cn.dansj.common.utils.transfer.ArrayUtils;
import cn.dansj.common.utils.transfer.Transformation;
import cn.dansj.common.utils.transfer.Verification;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Subquery;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class ArticleServiceImpl implements ArticleService {
    private final ArticleRepository articleRepository;

    public ArticleServiceImpl(ArticleRepository articleRepository) {
        this.articleRepository = articleRepository;
    }

    @Override
    public Page<Article> search(Integer pageNum, Integer pageLimit, String title, String type, int status) {
        Sort sort = Sort.by(Sort.Direction.DESC, "crtTime");
        Pageable pageable = PageRequest.of(pageNum - 1, pageLimit, sort);
        Specification<Article> specification = (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();
            if (Verification.checkNotNull(title)) {
                Expression<String> nameExpr = criteriaBuilder.lower(root.get("title"));
                predicateList.add(criteriaBuilder.like(nameExpr, Transformation.sqlLikeToLowCase(title)));
            }
            if (Verification.checkNotNull(type)) {
                Expression<String> connectExpr = criteriaBuilder.lower(root.get("type"));
                predicateList.add(criteriaBuilder.like(connectExpr, Transformation.sqlLikeToLowCase(type)));
            }
            if (ArrayUtils.asList(0, 1).contains(status)) {
                predicateList.add(criteriaBuilder.equal(root.get("status"), status));
            }

            Predicate[] predicates = new Predicate[predicateList.size()];
            return criteriaBuilder.and(predicateList.toArray(predicates));
        };
        return articleRepository.findAll(specification, pageable);
    }

    @Override
    public Page<Article> getList(String category, String tag, int pageNum, int pageLimit) {
        Sort sort = Sort.by(Sort.Direction.DESC, "crtTime");
        Pageable pageable = PageRequest.of(pageNum - 1, pageLimit, sort);
        Specification<Article> specification = (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();
            if (Verification.checkNotNull(category)) {
                predicateList.add(criteriaBuilder.equal(root.get("type"), category));
            }

            if (Verification.checkNotNull(tag)) {
                // exists查询
                Subquery<ArticleTag> subQuery = criteriaQuery.subquery(ArticleTag.class);
                Root<ArticleTag> subRoot = subQuery.from(ArticleTag.class);
                Predicate relative = criteriaBuilder.and(criteriaBuilder.equal(root.get("sequence"), subRoot.get("blogSequence")));
                Predicate tagIn = criteriaBuilder.equal(subRoot.get("tag"), tag);
                subQuery.select(subRoot.get("sequence")).where(relative, tagIn);
                Predicate existsHobby = criteriaBuilder.exists(subQuery);
                predicateList.add(existsHobby);
            }

            predicateList.add(criteriaBuilder.equal(root.get("status"), 1));
            Predicate[] predicates = new Predicate[predicateList.size()];
            return criteriaBuilder.and(predicateList.toArray(predicates));
        };
        return articleRepository.findAll(specification, pageable);
    }


    @Override
    public List<Article> getList() {
        return articleRepository.getList();
    }

    @Override
    public Article save(Article article) {
        return articleRepository.save(article);
    }

    @Override
    public List<Article> save(List<Article> articles) {
        return articleRepository.saveAll(articles);
    }

    @Override
    public Article detail(String sequence) {
        return articleRepository.getArticleBySequence(sequence);
    }

    @Override
    @Transactional
    public int delete(String sequence) {
        return articleRepository.deleteArticleBySequence(sequence);
    }

    @Override
    public List<Object[]> category() {
        return articleRepository.category();
    }

    @Override
    public List<ArticleTitle> search(String keyword) {
        List<Article> search = articleRepository.search(keyword);
        return search.stream().map(e -> DictMap.parseObject(e, ArticleTitle.class)).collect(Collectors.toList());
    }
}
